Skip to content

feat: distribute the Open Flow command release - #335

Open
l1shen wants to merge 6 commits into
oomol-lab:mainfrom
l1shen:horizontal-grouse
Open

feat: distribute the Open Flow command release#335
l1shen wants to merge 6 commits into
oomol-lab:mainfrom
l1shen:horizontal-grouse

Conversation

@l1shen

@l1shen l1shen commented Aug 6, 2026

Copy link
Copy Markdown
Contributor

No description provided.

@coderabbitai

coderabbitai Bot commented Aug 6, 2026

Copy link
Copy Markdown

Review Change Stack

Note

Reviews paused

It looks like this branch is under active development. To avoid overwhelming you with review comments due to an influx of new commits, CodeRabbit has automatically paused this review. You can configure this behavior by changing the reviews.auto_review.auto_pause_after_reviewed_commits setting.

Use the following commands to manage reviews:

  • @coderabbitai resume to resume automatic reviews.
  • @coderabbitai review to trigger a single review.

Use the checkboxes below for quick actions:

  • ▶️ Resume reviews
  • 🔍 Trigger review

No actionable comments were generated in the recent review. 🎉

ℹ️ Recent review info
⚙️ Run configuration

Configuration used: Organization UI

Review profile: CHILL

Plan: Pro

Run ID: ffb750fe-0282-4759-ac68-5497d456520b

📥 Commits

Reviewing files that changed from the base of the PR and between 67ce6e2 and 586e17a.

📒 Files selected for processing (2)
  • src/application/commands/flow-artifact.test.ts
  • src/application/commands/flow-artifact.ts
🚧 Files skipped from review as they are similar to previous changes (1)
  • src/application/commands/flow-artifact.test.ts

Summary by CodeRabbit

  • New Features

    • Added the oo flow command with argument forwarding, localization, Cloud access, uploads, team identity, progress reporting, and exit-code handling.
    • Open Flow artifacts are downloaded, validated, cached, and reused, with recovery from invalid caches.
    • The command appears in help on oomol.dev and supports local development through OO_OPEN_FLOW_COMMAND_DIR.
    • Added English and Chinese CLI messages, including setup, progress, and error feedback.
  • Documentation

    • Added comprehensive Open Flow usage and local testing guidance.
  • Improvements

    • Purge uninstall now removes cached Open Flow command artifacts.

Walkthrough

The CLI adds the oo flow command. It installs or loads a validated Open Flow artifact, forwards arguments, and provides locale, credentials, team identity, and restricted Cloud access. It validates archives, cache contents, entry modules, Bun versions, and exit codes. Endpoint-aware help and completion, telemetry redaction, localization, shared progress reporting, team headers, tests, documentation, and cache cleanup are included.

Sequence Diagram(s)

sequenceDiagram
  participant CLI as CLI bootstrap
  participant Flow as runOpenFlowCommand
  participant Artifact as Open Flow artifact installer
  participant Session as Cloud session
  participant Gateway as Cloud gateway
  CLI->>Flow: parse and delegate flow arguments
  Flow->>Artifact: resolve local or bundled artifact
  Artifact-->>Flow: return validated entry.js directory
  Flow->>Session: resolve account and team identity
  Session-->>Flow: return authorization and team headers
  Flow->>Gateway: send restricted request or upload
  Gateway-->>Flow: return Cloud response
  Flow-->>CLI: return validated exit code
Loading

Possibly related PRs

  • oomol-lab/oo-cli#332: Shares team-identity handling through teamIdentityHeaders and CLI integration.
🚥 Pre-merge checks | ✅ 3 | ❌ 1

❌ Failed checks (1 inconclusive)

Check name Status Explanation Resolution
Description check ❓ Inconclusive No pull request description was provided, so it does not explain the changeset. Add a brief description that summarizes the Open Flow command release, CLI integration, artifact handling, and related tests.
✅ Passed checks (3 passed)
Check name Status Explanation
Title check ✅ Passed The title uses the required format and accurately describes distributing the Open Flow command release.
Linked Issues check ✅ Passed Check skipped because no linked issues were found for this pull request.
Out of Scope Changes check ✅ Passed Check skipped because no linked issues were found for this pull request.
✨ Finishing Touches
✨ Simplify code
  • Create PR with simplified code

Comment @coderabbitai help to get the list of available commands.

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 2

🧹 Nitpick comments (7)
src/adapters/completion/static-completion-renderer.test.ts (1)

62-70: 📐 Maintainability & Code Quality | 🔵 Trivial | ⚡ Quick win

Import onlineDevEndpoint instead of hardcoding "oomol.dev".

createCliCatalog decides visibility with endpoint !== onlineDevEndpoint (src/application/commands/catalog.ts line 54). This test hardcodes the literal "oomol.dev". If the constant changes, the production catalog and this test drift apart.

Import the constant so the test tracks the single definition.

♻️ Proposed change
-        const devOutput = renderer.render("fish", createCliCatalog("oomol.dev"));
+        const devOutput = renderer.render("fish", createCliCatalog(onlineDevEndpoint));

Add the import alongside the existing catalog import, using the module that defines onlineDevEndpoint.

As per coding guidelines: "Never duplicate constant values across files. Define once, import or re-export with aliases elsewhere."

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@src/adapters/completion/static-completion-renderer.test.ts` around lines 62 -
70, Update the test around StaticCompletionRenderer to import onlineDevEndpoint
from the module that defines it and pass that constant to createCliCatalog
instead of the hardcoded "oomol.dev" value, keeping the visibility assertions
unchanged.

Source: Coding guidelines

src/application/commands/flow-artifact.test.ts (2)

119-134: 📐 Maintainability & Code Quality | 🔵 Trivial | ⚡ Quick win

Add coverage for tar path traversal rejection.

The suite covers the typeflag check with a link entry. It does not cover isNormalizedArtifactPath, which is the control that stops an archive entry from writing outside the extraction directory.

Add cases that assert rejection for an entry path containing a .. segment and for an absolute path. Both go through the same encodeTarGzip helper already in this file.

💚 Suggested additional cases
+    test("rejects tar entries that escape the artifact root", async () => {
+        const archive = encodeTarGzip([{
+            body: new Uint8Array(),
+            mode: 0o644,
+            path: "open-flow-command/../escape.js",
+            type: "0",
+        }]);
+        const release = createRelease(archive);
+        const environment = await createTestEnvironment();
+
+        await expect(installOpenFlowCommandRelease(release, {
+            env: environment.env,
+            execPath: process.execPath,
+            fetcher: createArchiveFetcher(archive),
+        })).rejects.toThrow("invalid file path");
+    });
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@src/application/commands/flow-artifact.test.ts` around lines 119 - 134, Add
test coverage in the flow artifact installation tests for
isNormalizedArtifactPath by creating archives with one entry using a path
containing a ".." segment and another using an absolute path. Reuse
encodeTarGzip and the existing installOpenFlowCommandRelease assertion pattern,
verifying both cases reject before extraction.

136-166: 📐 Maintainability & Code Quality | 🔵 Trivial | ⚡ Quick win

The concurrency test passes even when no serialization occurs.

The test resolves resumeRequest immediately after creating second. Nothing guarantees that second reaches acquireDownloadTempLock before first completes its install. If first finishes first, second takes the cache-hit return at flow-artifact.ts line 133 and never contends the lock. requestCount is still 1 and both assertions still pass.

The test therefore cannot distinguish lock serialization from plain cache reuse.

To make the assertion meaningful, hold the first request open until second has demonstrably started, then release:

💚 Suggested restructure
         const first = installOpenFlowCommandRelease(fixture.release, options);
         await requestStarted.promise;
         const second = installOpenFlowCommandRelease(fixture.release, options);
-        resumeRequest.resolve();
+        // Give `second` time to reach the lock while the first download is still blocked.
+        await Bun.sleep(50);
+        expect(requestCount).toBe(1);
+        resumeRequest.resolve();

This proves the second call did not issue its own request while the first held the lock.

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@src/application/commands/flow-artifact.test.ts` around lines 136 - 166,
Restructure the test “serializes concurrent installation of the same digest” so
the first fetch remains blocked until the second installation has demonstrably
started and attempted to contend with the download lock. Add an explicit
synchronization signal for the second call’s fetch/lock-entry path, await that
signal before resolving resumeRequest, then retain the assertions that both
calls share the directory and requestCount is one.
src/application/commands/flow-artifact.ts (3)

133-135: 🚀 Performance & Scalability | 🔵 Trivial | ⚡ Quick win

Every oo flow invocation re-hashes the whole cached artifact.

Line 133 calls validCommandArtifactDirectory before any lock is taken, on the normal cache-hit path. That runs validateCommandArtifactDirectory, which walks the directory tree and then reads and SHA-256 hashes every manifest-listed file (lines 651-654). The pinned release archive is about 5 MB, so each oo flow invocation pays a full read and hash of the expanded artifact before Open Flow starts.

The cache directory is already named by the archive digest and is written through an atomic rename from a private extraction directory, so a correct cache entry cannot be partially written. The full re-hash defends only against post-install local tampering or disk corruption.

Consider a cheaper steady-state check and keep the full verification for the repair path. Options:

  • Compare the file set plus each file's size and mtime against a stamp written at install time, and fall back to full hashing only on mismatch.
  • Write a .verified marker containing the digest after a successful install, and validate fully only when the marker is absent.
♻️ Sketch: fast path first, full validation as fallback
-    if (await validCommandArtifactDirectory(commandDirectory, release)) {
+    if (await cachedArtifactLooksIntact(commandDirectory, release)) {
         return commandDirectory;
     }

Add a helper that compares the recorded file set, sizes, and mtimes, and only calls validateCommandArtifactDirectory when that comparison fails.

Also applies to: 629-655

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@src/application/commands/flow-artifact.ts` around lines 133 - 135, Update the
cache-hit logic around validCommandArtifactDirectory so normal oo flow
invocations use a cheap integrity check first, comparing the recorded artifact
file set, sizes, and mtimes from installation; invoke
validateCommandArtifactDirectory only when that check fails or no install marker
exists. Preserve the existing full validation and repair behavior for
mismatches, and apply the same fast-path handling to the related validation flow
near the artifact install logic.

219-291: 🩺 Stability & Availability | 🔵 Trivial | ⚡ Quick win

Add a timeout or idle-progress guard to downloadCommandArchive.

Fetcher and createRetryingFetcher do not impose a deadline. A stalled response can block oo flow indefinitely while holding the download lock. Pass an AbortSignal deadline or add an idle-byte watchdog, consistent with the self-update download path.

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@src/application/commands/flow-artifact.ts` around lines 219 - 291, Update
downloadCommandArchive to enforce a download deadline or idle-progress timeout,
using the same timeout/watchdog approach as the self-update download path. Apply
it to the fetch request and ensure stalled responses abort and release the
reader and file handle instead of blocking indefinitely.

363-380: 📐 Maintainability & Code Quality | 🔵 Trivial | ⚡ Quick win

Avoid coupling archive acceptance to local compressor output.

downloadCommandArchive checks the complete response length and SHA-256 digest before decodeCommandArchive runs. The full gzipSync(tar, { level: 9 }) comparison adds a compressor-output constraint, not archive integrity. Different node:zlib implementations or versions can reject the pinned archive with "not canonically encoded". Replace it with an implementation-independent check for trailing or truncated gzip data if that validation remains required.

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@src/application/commands/flow-artifact.ts` around lines 363 - 380, Update
decodeCommandArchive and canonicalGzip so archive acceptance no longer depends
on local gzipSync output; retain the existing full-length and SHA-256 validation
in downloadCommandArchive, and replace the compressor comparison with an
implementation-independent check that rejects truncated or trailing gzip data if
needed.
src/application/bootstrap/run-cli.ts (1)

338-344: 🗄️ Data Integrity & Integration | 🔵 Trivial | ⚡ Quick win

Report the real delegated argument count.

argCount and flagsCount are always 0, even when openFlowInvocation.args is not empty. The flow telemetry event therefore reports every invocation as argument-free, and the dimension carries no signal. An argument count is a count, not free-form input, so it stays privacy safe. Prefer a bucket helper from src/application/telemetry/buckets.ts if one exists for counts.

♻️ Proposed change
             telemetryRecorder.observer.onCommandResolved?.({
-                argCount: 0,
+                argCount: openFlowInvocation.args.length,
                 commandPath: ["flow"],
                 excludeFromTelemetry: false,
-                flagsCount: 0,
+                flagsCount: openFlowInvocation.args.filter(
+                    argument => argument.startsWith("-"),
+                ).length,
                 outputFormat: "text",
             });

As per coding guidelines: "For useful command-specific dimensions, call context.telemetry?.recordProperties(...) from the command handler with only low-cardinality, privacy-safe enums, booleans, counts, or buckets."

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@src/application/bootstrap/run-cli.ts` around lines 338 - 344, Update the flow
telemetry payload in the command-resolution path to report the real delegated
argument count from openFlowInvocation.args instead of hardcoding argCount to
zero, and derive flagsCount from the delegated flags when available. Reuse the
existing count-bucketing helper from buckets.ts if applicable, while preserving
the privacy-safe, low-cardinality telemetry contract.

Source: Coding guidelines

🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Inline comments:
In `@src/application/bootstrap/run-cli.ts`:
- Around line 337-353: Verify the intended endpoint scope for the open-flow path
in the run-cli flow invocation branch. If flow is restricted to oomol.dev, guard
the runOpenFlowCommand call and its related telemetry with the same OO_ENDPOINT
check used by createCliCatalog; otherwise preserve execution on all endpoints
and ensure the catalog visibility behavior is intentional.

In `@src/application/commands/flow-artifact.ts`:
- Around line 732-750: Update the purge handling in the uninstall command to
remove both storePaths.dataDirectory and the Open Flow artifact cache resolved
by resolveCommandCacheRoot. Ensure --purge deletes the platform-specific cache
root in addition to the existing local SQLite data cache, while leaving
non-purge uninstall behavior unchanged.

---

Nitpick comments:
In `@src/adapters/completion/static-completion-renderer.test.ts`:
- Around line 62-70: Update the test around StaticCompletionRenderer to import
onlineDevEndpoint from the module that defines it and pass that constant to
createCliCatalog instead of the hardcoded "oomol.dev" value, keeping the
visibility assertions unchanged.

In `@src/application/bootstrap/run-cli.ts`:
- Around line 338-344: Update the flow telemetry payload in the
command-resolution path to report the real delegated argument count from
openFlowInvocation.args instead of hardcoding argCount to zero, and derive
flagsCount from the delegated flags when available. Reuse the existing
count-bucketing helper from buckets.ts if applicable, while preserving the
privacy-safe, low-cardinality telemetry contract.

In `@src/application/commands/flow-artifact.test.ts`:
- Around line 119-134: Add test coverage in the flow artifact installation tests
for isNormalizedArtifactPath by creating archives with one entry using a path
containing a ".." segment and another using an absolute path. Reuse
encodeTarGzip and the existing installOpenFlowCommandRelease assertion pattern,
verifying both cases reject before extraction.
- Around line 136-166: Restructure the test “serializes concurrent installation
of the same digest” so the first fetch remains blocked until the second
installation has demonstrably started and attempted to contend with the download
lock. Add an explicit synchronization signal for the second call’s
fetch/lock-entry path, await that signal before resolving resumeRequest, then
retain the assertions that both calls share the directory and requestCount is
one.

In `@src/application/commands/flow-artifact.ts`:
- Around line 133-135: Update the cache-hit logic around
validCommandArtifactDirectory so normal oo flow invocations use a cheap
integrity check first, comparing the recorded artifact file set, sizes, and
mtimes from installation; invoke validateCommandArtifactDirectory only when that
check fails or no install marker exists. Preserve the existing full validation
and repair behavior for mismatches, and apply the same fast-path handling to the
related validation flow near the artifact install logic.
- Around line 219-291: Update downloadCommandArchive to enforce a download
deadline or idle-progress timeout, using the same timeout/watchdog approach as
the self-update download path. Apply it to the fetch request and ensure stalled
responses abort and release the reader and file handle instead of blocking
indefinitely.
- Around line 363-380: Update decodeCommandArchive and canonicalGzip so archive
acceptance no longer depends on local gzipSync output; retain the existing
full-length and SHA-256 validation in downloadCommandArchive, and replace the
compressor comparison with an implementation-independent check that rejects
truncated or trailing gzip data if needed.
🪄 Autofix

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: Organization UI

Review profile: CHILL

Plan: Pro

Run ID: abd90123-9b87-40dd-a205-928ac3e0966a

📥 Commits

Reviewing files that changed from the base of the PR and between 2529e32 and 0d05f67.

📒 Files selected for processing (19)
  • docs/commands.md
  • docs/commands.zh-CN.md
  • src/adapters/completion/static-completion-renderer.test.ts
  • src/application/bootstrap/run-cli.ts
  • src/application/commands/catalog.ts
  • src/application/commands/connector/shared.ts
  • src/application/commands/file/download.ts
  • src/application/commands/file/download/file-system.test.ts
  • src/application/commands/file/download/file-system.ts
  • src/application/commands/flow-artifact.test.ts
  • src/application/commands/flow-artifact.ts
  • src/application/commands/flow-release.ts
  • src/application/commands/flow.cli.test.ts
  • src/application/commands/flow.ts
  • src/application/commands/shared/download-progress.test.ts
  • src/application/commands/shared/download-progress.ts
  • src/application/commands/team/identity.ts
  • src/application/commands/telemetry-decisions.test.ts
  • src/i18n/catalog.ts

Comment thread src/application/bootstrap/run-cli.ts
Comment thread src/application/commands/flow-artifact.ts Outdated
@l1shen

l1shen commented Aug 6, 2026

Copy link
Copy Markdown
Contributor Author

Review follow-up for the non-threaded suggestions I am not adopting:

  • Endpoint constant: keeping onlineDevEndpoint module-private. The completion test deliberately asserts the literal public contract oomol.dev instead of importing an implementation detail.
  • Cache fast path: retaining full manifest, file-set, and SHA-256 validation on cache hits so tampering or disk corruption is detected and repaired; no marker/stat shortcut without evidence that integrity validation is a bottleneck.
  • Delegated telemetry counts: retaining the documented decision not to inspect Open Flow arguments or flags because delegated values may contain paths, payloads, or tokens. The zero counts intentionally mean not inspected.

The actionable archive timeout, tar-path, concurrency, gzip compatibility, and uninstall purge feedback is addressed in commit 8e45c65.

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 1

🧹 Nitpick comments (1)
src/application/self-update/uninstall.test.ts (1)

203-209: 📐 Maintainability & Code Quality | 🔵 Trivial | ⚡ Quick win

Use resolveOpenFlowCommandCacheRoot for the expected path.

This test duplicates the platform and cache-format rules from resolveOpenFlowCommandCacheRoot. Build the expected value with that resolver and the same environment, home directory, and platform used to build the uninstall plan.

Proposed fix
-        expect(paths(userData)).toContain(join(
-            tempHome,
-            ".cache",
-            "oo",
-            "open-flow",
-            "command-artifact-v1",
-        ));
+        expect(paths(userData)).toContain(
+            resolveOpenFlowCommandCacheRoot({
+                env: { HOME: tempHome },
+                homeDirectory: tempHome,
+                platform: "linux",
+            }),
+        );

As per coding guidelines, replace test expressions that duplicate extracted production logic with the shared utility.

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@src/application/self-update/uninstall.test.ts` around lines 203 - 209, Update
the expected cache path assertion in the uninstall test to use
resolveOpenFlowCommandCacheRoot with the same environment, home directory, and
platform passed when constructing the uninstall plan, instead of manually
joining cache path segments. Keep the assertion verifying that paths(userData)
contains the resolver’s returned value.

Source: Coding guidelines

🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Inline comments:
In `@src/application/commands/flow-artifact.test.ts`:
- Around line 158-173: Update the “guards archive downloads with a timeout
signal” test to use fake timers and make its createArchiveFetcher callback
reject when the captured signal emits abort. Advance the timer by the configured
300,000 ms timeout, assert installOpenFlowCommandRelease rejects, and verify the
installation cleanup removes temporary files.

---

Nitpick comments:
In `@src/application/self-update/uninstall.test.ts`:
- Around line 203-209: Update the expected cache path assertion in the uninstall
test to use resolveOpenFlowCommandCacheRoot with the same environment, home
directory, and platform passed when constructing the uninstall plan, instead of
manually joining cache path segments. Keep the assertion verifying that
paths(userData) contains the resolver’s returned value.
🪄 Autofix

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: Organization UI

Review profile: CHILL

Plan: Pro

Run ID: 7655e718-c33b-4261-9436-66086c3ca260

📥 Commits

Reviewing files that changed from the base of the PR and between 0d05f67 and 8e45c65.

📒 Files selected for processing (5)
  • src/application/commands/flow-artifact.test.ts
  • src/application/commands/flow-artifact.ts
  • src/application/commands/uninstall.cli.test.ts
  • src/application/self-update/uninstall.test.ts
  • src/application/self-update/uninstall.ts
🚧 Files skipped from review as they are similar to previous changes (1)
  • src/application/commands/flow-artifact.ts

Comment thread src/application/commands/flow-artifact.test.ts Outdated

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🧹 Nitpick comments (1)
src/application/commands/flow-artifact.ts (1)

266-305: 🩺 Stability & Availability | 🔵 Trivial | ⚡ Quick win

Cancel the response body reader on the failure path.

The inner finally calls reader.releaseLock() but never cancels the stream. If invalid(...) throws at Line 275 or Line 289, the remaining body is left unread and the connection is held until the abort timer fires at commandArchiveDownloadTimeoutMs. Cancel the reader so the socket is released immediately.

♻️ Proposed change to cancel the reader before releasing the lock
         finally {
-            reader.releaseLock();
+            await reader.cancel().catch(() => {});
+            reader.releaseLock();
             await fileHandle.close();
         }
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@src/application/commands/flow-artifact.ts` around lines 266 - 305, Update the
download cleanup in the inner finally block around reader and fileHandle so the
response body reader is cancelled before releaseLock(), ensuring failures from
invalid(...) terminate the remaining stream immediately while preserving the
existing file close behavior.
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Nitpick comments:
In `@src/application/commands/flow-artifact.ts`:
- Around line 266-305: Update the download cleanup in the inner finally block
around reader and fileHandle so the response body reader is cancelled before
releaseLock(), ensuring failures from invalid(...) terminate the remaining
stream immediately while preserving the existing file close behavior.

ℹ️ Review info
⚙️ Run configuration

Configuration used: Organization UI

Review profile: CHILL

Plan: Pro

Run ID: 0b851dbe-8ad2-4ec9-84d5-48b6658ea284

📥 Commits

Reviewing files that changed from the base of the PR and between 02a324f and 67ce6e2.

📒 Files selected for processing (1)
  • src/application/commands/flow-artifact.ts

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant